package com.yang.frost.raspberry.shiro.jwt;

import com.yang.frost.raspberry.config.properties.JwtProperties;
import com.yang.frost.raspberry.enums.ApiCode;
import com.yang.frost.raspberry.shiro.service.LoginRedisService;
import com.yang.frost.raspberry.shiro.service.ShiroLoginService;
import com.yang.frost.raspberry.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.rmi.runtime.Log;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 10296
 * @date 2020/12/24
 * @Description:
 */
public class JwtFilter extends AuthenticatingFilter {
    private Logger logger = LoggerFactory.getLogger(JwtFilter.class);

    private ShiroLoginService shiroLoginService;
    private LoginRedisService loginRedisService;
    private JwtProperties jwtProperties;

    /**
     * 将JWT Token 包装成 AuthenticationToken
     *
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        String token = JwtTokenUtil.getToken();
        if (StringUtils.isEmpty(token)) {
            throw new AuthenticationException("token不能为空！");
        }
        if (JwtUtil.isExpired(token)) {
            throw new AuthenticationException("JWT Token 已过期，token：" + token);
        }
        // 如果开启redis二次校验，或者设置为单个用户token登录，则先在redis中判断token是否存在
        //
        boolean isRedisCheck = true;
        boolean isSingleLogin = true;
        loginRedisService = SpringContextUtil.getBean(LoginRedisService.class);
        jwtProperties = SpringContextUtil.getBean(JwtProperties.class);

        if (jwtProperties.isRedisCheck() || jwtProperties.isSingleLogin()) {
//        if (isRedisCheck || isSingleLogin) {
            boolean redisExpired = loginRedisService.exists(token);
            if (!redisExpired) {
                throw new AuthenticationException("Redis Token不存在,token:" + token);
            }
        }

        String username = JwtUtil.getUsername(token);
        String salt;
        boolean isSaltCheck = true;
//        if (isSaltCheck) {
        if (jwtProperties.isSaltCheck()) {
            salt = loginRedisService.getSalt(username);
        } else {
            salt = jwtProperties.getSecret();
        }


        return JwtToken.build(token, username, salt, jwtProperties.getExpireSecond());

    }

    /**
     * 访问失败
     *
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(servletRequest);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(servletResponse);
        httpServletResponse.setStatus(401);
        //设置响应码为401或者直接输出消息
        String url = httpServletRequest.getRequestURI();
        logger.error("onAccessDenied url：{}", url);
        ApiResult<Boolean> apiResult = ApiResult.fail(ApiCode.UNAUTHORIZED);
        HttpServletResponseUtil.printJson(httpServletResponse, apiResult);
        return false;
    }

    /**
     * 判断是否允许访问
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        String url = WebUtils.toHttp(request).getRequestURI();
        logger.info("isAccessAllowed url:{}", url);
        if (this.isLoginRequest(request, response)) {
            return true;
        }
        boolean allowed = false;
        try {
            allowed = executeLogin(request, response);

        } catch (Exception e) {
            logger.error(">>>>>>>>isAccessAllowed>>>>>>>", e);
            e.printStackTrace();
        }
        return allowed || super.isPermissive(mappedValue);

    }

    /**
     * 登录成功处理
     *
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {

        shiroLoginService = SpringContextUtil.getBean(ShiroLoginService.class);
        String url = WebUtils.toHttp(request).getRequestURI();
        logger.info("鉴权成功，token:{},url:{}", token, url);
        JwtToken jwtToken = (JwtToken) token;
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        shiroLoginService.refreshToken(jwtToken, httpServletResponse);

        return true;

    }

    /**
     * 登录失败
     *
     * @param token
     * @param e
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        logger.error("登录失败，token:" + token + ",error:" + e.getMessage(), e);
        return false;

    }
}
